﻿using System.IO;
using Exceptions;

namespace RSA
{
    /// <summary>
    /// A BLOBHEADER / PUBLICKEYSTRUC structure (Import from WinCrypt.h)
    /// </summary>
    /// <note>http://msdn.microsoft.com/en-us/library/ms884652.aspx</note>
    internal struct BlobHeader
    {
        /// <summary>
        /// Key BLOB type. The only BLOB types currently defined are PUBLICKEYBLOB, PRIVATEKEYBLOB, and SIMPLEBLOB. Other key BLOB types will be defined as needed.
        /// </summary>
        public KeyBlobType BlobType;

        /// <summary>
        /// Version number of the key BLOB format. This member currently must always have a value of 0x02.
        /// </summary>
        public byte Version;

        /// <summary>
        /// Reserved for future use. This member must be set to zero.
        /// </summary>
        public ushort Reserved;

        /// <summary>
        /// Algorithm identifier for the key contained by the key BLOB structure
        /// </summary>
        public AlgorithmIdentifier KeyAlgorithm;

        #region FromBinary - Create and initialize structure from binary data

        /// <summary>
        /// Create and initialize structure from binary data
        /// </summary>
        /// <exception cref="CryptographicException">On validate errors</exception>
        /// <returns>Initialized structure</returns>
        public static BlobHeader FromBinary(BinaryReader reader)
        {
            var blobHeader = new BlobHeader
            {
                BlobType = (KeyBlobType)reader.ReadByte(),
                Version = reader.ReadByte(),
                Reserved = reader.ReadUInt16(),
                KeyAlgorithm = (AlgorithmIdentifier)reader.ReadUInt32()
            };

            // Validate
            if (blobHeader.BlobType != KeyBlobType.PublicKeyBlob && blobHeader.BlobType != KeyBlobType.PrivateKeyBlob)
                throw new CryptographicException(string.Format("Unsupported Key BLOB type [{0}] in BlobHeader",
                                                               blobHeader.BlobType));

            if (blobHeader.Version != 0x02)
                throw new CryptographicException(string.Format("Unknown version [{0}] in BlobHeader",
                                                               blobHeader.Version));

            if (blobHeader.KeyAlgorithm != AlgorithmIdentifier.CALG_RSA_KEYX)
                throw new CryptographicException(
                    string.Format("Unsupported algorithm identifier [{0:X4}] in BlobHeader",
                                  (uint)blobHeader.KeyAlgorithm));
            return blobHeader;
        }

        #endregion

        #region ToBinary - Serializes structure as binary data

        /// <summary>
        /// Serializes structure as binary data
        /// </summary>
        public void ToBinary(BinaryWriter writer)
        {
            writer.Write((byte)BlobType);
            writer.Write(Version);
            writer.Write(Reserved);
            writer.Write((uint)KeyAlgorithm);
        }

        #endregion

        #region FromRSAParameters - Create and initialize structure from RSAParameters

        /// <summary>
        /// Create and initialize structure from RSAParameters
        /// </summary>
        /// <returns>Initialized structure</returns>
        public static BlobHeader FromRSAParameters(KeyBlobType blobType)
        {
            var blobHeader = new BlobHeader
            {
                BlobType = blobType,
                Version = 0x02,
                Reserved = 0x0000,
                KeyAlgorithm = AlgorithmIdentifier.CALG_RSA_KEYX
            };

            return blobHeader;
        }

        #endregion

        #region ToString overriding

        /// <summary>
        /// Returns a <see cref="System.String"/> that represents this instance.
        /// </summary>
        /// <returns>
        /// A <see cref="System.String"/> that represents this instance.
        /// </returns>
        public override string ToString()
        {
            return string.Format("BlobType: {0} | Version: {1} | KeyAlgorithm: {2}", BlobType, Version, KeyAlgorithm);
        }

        #endregion
    }
}
